#include "common.h"

void generate_random_players(std::vector<player> & players, int32_t count, const int32_t width, const int32_t height, const int32_t max_speed /*= max_axis_speed*/)
{
    players.resize(count);
    srand((uint32_t)time(nullptr));
    for(int32_t i = 0; i < count; ++i)
    {
        int32_t px = rand() % width;
        int32_t py = rand() % height;
        int32_t factor = rand() % 2 ? 1 : -1;
        int32_t sx = (rand() % max_speed + max_speed / 2) * factor;
        int32_t sy = (rand() % max_speed + max_speed / 2) * factor;
        players[i].point = point2d_int(px, py);
        players[i].speed = point2d_int(sx, sy);
    }
}

clock_t player_move(std::vector<player> & players, const clock_t last_clock, const int32_t width, const int32_t height)
{
    clock_t now_clock = clock();
    clock_t frame_time = now_clock - last_clock;
    for(uint32_t i = 0; i < players.size(); ++i)
    {
        point2d_int & pos = players[i].point;
        point2d_int & spd = players[i].speed;
        if(pos.x < 0)
        {
            pos.x = 0;
            spd.x *= -1;
        }
        if(pos.x > width)
        {
            pos.x = width;
            spd.x *= -1;
        }
        if(pos.y < 0)
        {
            pos.y = 0;
            spd.y *= -1;
        }
        if(pos.y > height)
        {
            pos.y = height;
            spd.y *= -1;
        }
        pos.x += int32_t(frame_time * spd.x / 1000.0f);
        pos.y += int32_t(frame_time * spd.y / 1000.0f);
    }
    return now_clock;
}

get_aoi_ptr rander_window::get_aoi = nullptr;
HPEN rander_window::white_pen = nullptr;
HPEN rander_window::black_pen = nullptr;
HPEN rander_window::black_dot_pen = nullptr;
std::vector<player> rander_window::players;
std::vector<int32_t> rander_window::aoi_players;
int32_t rander_window::sel_index = 0;

void rander_window::init(TCHAR * windows_name, HINSTANCE instance, int32_t cmd_show, get_aoi_ptr ptr, TCHAR * wnd_class_name /*= "rander_window"*/)
{
    HWND            hwnd;
    MSG             msg;
    WNDCLASS        wndclass;

    get_aoi = ptr;

    wndclass.style = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc = wnd_proc;
    wndclass.cbClsExtra = 0;
    wndclass.cbWndExtra = 0;
    wndclass.hInstance = instance;
    wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
    wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wndclass.lpszMenuName = NULL;
    wndclass.lpszClassName = wnd_class_name;

    if(!RegisterClass(&wndclass))
    {
        MessageBox(NULL, TEXT("Program requires Windows NT!"), windows_name, MB_ICONERROR);
        return;
    }

    hwnd = CreateWindow(wnd_class_name, windows_name,
                        WS_OVERLAPPEDWINDOW ^ WS_THICKFRAME,
                        CW_USEDEFAULT, CW_USEDEFAULT,
                        CW_USEDEFAULT, CW_USEDEFAULT,
                        NULL, NULL, instance, NULL);

    ShowWindow(hwnd, cmd_show);
    UpdateWindow(hwnd);

    while(GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}

LRESULT CALLBACK rander_window::wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static int32_t cx = 0, cy = 0;
    switch(message)
    {
    case WM_CREATE:
        white_pen = CreatePen(PS_SOLID, 3, RGB(0, 0, 0));
        black_pen = CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
        black_dot_pen = CreatePen(PS_DOT, 1, RGB(0, 0, 0));
        SetTimer(hwnd, timer_id, 30, NULL);
        break;
    case WM_SIZE:
        cx = LOWORD(lParam);
        cy = HIWORD(lParam);
        break;
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hwnd, &ps);
            do_rander(hwnd, hdc, cx, cy);
            EndPaint(hwnd, &ps);
        }
        break;
    case WM_TIMER:
        switch(wParam)
        {
        case timer_id:
            InvalidateRect(hwnd, NULL, false);
            break;
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        GetStockObject(WHITE_BRUSH);
        KillTimer(hwnd, timer_id);
        DeleteObject(white_pen);
        DeleteObject(black_pen);
        return 0;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}

void rander_window::do_rander(HWND hwnd, HDC hdc, int32_t cx, int32_t cy, bool is_double /*= true*/)
{
    if(is_double)
    {
        HDC mdc = CreateCompatibleDC(hdc);
        HBITMAP memBitmap = CreateCompatibleBitmap(hdc, cx, cy);
        HANDLE old_handle = SelectObject(mdc, memBitmap);
        rander(hwnd, mdc, cx, cy);
        BitBlt(hdc, 0, 0, cx, cy, mdc, 0, 0, SRCCOPY);
        SelectObject(mdc, old_handle);
        DeleteObject(memBitmap);
        DeleteDC(mdc);
        return;
    }
    rander(hwnd, hdc, cx, cy);
}

void rander_window::rander(HWND hwnd, HDC hdc, int32_t cx, int32_t cy)
{
    SelectObject(hdc, white_pen);
    Rectangle(hdc, 0, 0, cx, cy);
    const int32_t world_witdh = cy;
    const int32_t self_width = cx - cy;
    MoveToEx(hdc, world_witdh, 0, nullptr);
    LineTo(hdc, world_witdh, world_witdh);
    MoveToEx(hdc, world_witdh, self_width, nullptr);
    LineTo(hdc, cx, self_width);
    {
        if(players.empty())
        {
            generate_random_players(players, player_count, world_witdh, world_witdh);
        }
        (*get_aoi)(players, sel_index, aoi_players, range);
        static clock_t last_clock = 0;
        last_clock = player_move(players, last_clock, world_witdh, world_witdh);
    }
    {
        SelectObject(hdc, black_pen);
        for(uint32_t i = 0; i < players.size(); ++i)
        {
            draw_player_point(hdc, players[i].point, dot_size);
        }
        SelectObject(hdc, black_dot_pen);
        draw_dot_range(hdc, players[aoi_players[sel_index]].point, range);
        SelectObject(hdc, black_pen);
        RECT self_range;
        SetRect(&self_range, world_witdh, 0, cx, self_width);
        for(uint32_t i = 0; i < aoi_players.size(); ++i)
        {
            point2d_int pt = point_trans(players[aoi_players[i]].point, players[aoi_players[sel_index]].point, range, self_range);
            draw_player_point(hdc, pt, dot_size);
        }
    }
}

void rander_window::draw_player_point(HDC hdc, const point2d_int pt, const int32_t size)
{
    Ellipse(hdc, pt.x - size / 2, pt.y - size / 2, pt.x + size / 2, pt.y + size / 2);
}

void rander_window::draw_dot_range(HDC hdc, const point2d_int pt, const int32_t range)
{
    MoveToEx(hdc, pt.x - range, pt.y - range, nullptr);
    LineTo(hdc, pt.x + range, pt.y - range);
    LineTo(hdc, pt.x + range, pt.y + range);
    LineTo(hdc, pt.x - range, pt.y + range);
    LineTo(hdc, pt.x - range, pt.y - range);
}

point2d_int rander_window::point_trans(const point2d_int ori, const point2d_int center, const int32_t range, RECT dest_rect)
{
    point2d_int pt;
    RECT src_rect;
    SetRect(&src_rect, center.x - range, center.y - range, center.x + range, center.y + range);
    pt.x = (ori.x - src_rect.left) * (dest_rect.right - dest_rect.left) / (src_rect.right - src_rect.left) + dest_rect.left;
    pt.y = (ori.y - src_rect.top) * (dest_rect.bottom - dest_rect.top) / (src_rect.bottom - src_rect.top) + dest_rect.top;
    return pt;
}

